package cn.fetosoft.woodpecker.controller;

import cn.fetosoft.woodpecker.cluster.StartTestEvent;
import cn.fetosoft.woodpecker.controller.attribute.MoveElement;
import cn.fetosoft.woodpecker.core.data.entity.BaseElement;
import cn.fetosoft.woodpecker.core.data.entity.ElementTree;
import cn.fetosoft.woodpecker.core.data.entity.element.MultiArguments;
import cn.fetosoft.woodpecker.core.data.form.ElementForm;
import cn.fetosoft.woodpecker.core.data.service.ElementService;
import cn.fetosoft.woodpecker.core.enums.ElementCategory;
import cn.fetosoft.woodpecker.core.util.Constant;
import cn.fetosoft.woodpecker.core.util.MenuTree;
import cn.fetosoft.woodpecker.core.util.RandomUtil;
import cn.fetosoft.woodpecker.struct.UserIdentity;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * @author guobingbing
 * @version 1.0
 * @wechat t_gbinb
 * @create 2021/6/11 10:22
 */
@Slf4j
@Controller
@RequestMapping(value = "/auth/element", produces = "application/json")
public class ElementController extends BaseController implements ApplicationContextAware {

	@Autowired
	private ElementService elementService;
	private ApplicationContext applicationContext;

	@RequestMapping("/gotoPage")
	public String gotoPage(@RequestParam String page, @RequestParam String id, HttpServletRequest request){
		request.setAttribute("eid", id);
		ElementCategory category = ElementCategory.getCategory(page);
		if(category!=null){
			page = "/element/" + category.getElementPage();
		}else{
			page = "404";
		}
		return page;
	}

	/**
	 * 启动测试
	 * @param body
	 * @return
	 */
	@PostMapping("/startTest")
	@ResponseBody
	public String startTest(@RequestBody String body, HttpServletRequest request){
		Result<String> result = Result.errorResult();
		try{
			UserIdentity identity = this.getSessionUser(request);
			JSONObject json = JSONObject.parseObject(body);
			String planId = json.getString("planId");
			String testId = RandomUtil.uuid();
			this.applicationContext.publishEvent(new StartTestEvent(identity.getUserId(), testId, planId));
			result.setStatus(Result.SUCCESS);
		}catch(Exception e){
			result.setErrorMsg(e.getMessage());
			log.error("startTest", e);
		}
		return result.toString();
	}

	/**
	 * 获取组件详情
	 * @param id
	 * @return
	 */
	@GetMapping("/getDetail")
	@ResponseBody
	public String getDetail(@RequestParam String id){
		Result<BaseElement> result = Result.errorResult();
		if(StringUtils.isNotBlank(id) && !id.equals("0")) {
			BaseElement element = (BaseElement) elementService.findById(id, BaseElement.class);
			result.setObj(element);
			result.setStatus(Result.SUCCESS);
		}
		return result.toString();
	}

	/**
	 * 查询测试计划字典
	 * @return
	 */
	@GetMapping("/getPlansDict")
	@ResponseBody
	public String getPlansDict(){
		JSONArray array = new JSONArray();
		ElementForm form = new ElementForm();
		form.setRows(0);
		form.setCategory(ElementCategory.TEST_PLAN.getName());
		try {
			List<BaseElement> list = elementService.selectListByForm(form, BaseElement.class);
			for(BaseElement e : list){
				JSONObject json = new JSONObject();
				json.put("id", e.getId());
				json.put("text", e.getName());
				array.add(json);
			}
		} catch (Exception e) {
			log.error("getPlansDict", e);
		}
		return array.toJSONString();
	}

	/**
	 * 创建
	 * @param body
	 * @return
	 */
	@PostMapping("/create")
	@ResponseBody
	public String create(@RequestBody String body, HttpServletRequest request){
		Result<BaseElement> result = Result.errorResult();
		try{
			JSONObject json = JSONObject.parseObject(body);
			ElementCategory category = ElementCategory.getCategory(json.getString("category"));
			if(category==null){
				result.setErrorMsg("Category类型不正确");
				return result.toString();
			}
			BaseElement baseElement = JSON.toJavaObject(json, category.getElementClass());
			UserIdentity identity = this.getSessionUser(request);
			baseElement.setUserId(identity.getUserId());
			if(StringUtils.isBlank(baseElement.getName())) {
				baseElement.setName(category.getText());
			}
			baseElement.setEnabled(true);
			if(category == ElementCategory.TEST_PLAN){
				baseElement.setPlanId(RandomUtil.uuid());
			}else if(category == ElementCategory.TEST_FRAGMENT){
				//判断是否为测试片段
				baseElement.setEnabled(false);
			}
			String sortPlanId = baseElement.getPlanId();
			if(Constant.ROOT_PARENT_ID.equals(baseElement.getParentId())){
				sortPlanId = null;
			}
			int sort = elementService.findMaxSort(sortPlanId, baseElement.getParentId(), baseElement.getClass());
			baseElement.setSort(sort);
			baseElement = (BaseElement) elementService.insert(baseElement);
			result.setObj(baseElement);
			result.setStatus(Result.SUCCESS);
		}catch(Exception e){
			result.setErrorMsg(e.getMessage());
			log.error("create", e);
		}
		return result.toString();
	}

	/**
	 * 修改信息
	 * @param body
	 * @return
	 */
	@PostMapping("/update")
	@ResponseBody
	public String update(@RequestBody String body){
		Result<JSONObject> result = Result.errorResult();
		try {
			JSONObject json = JSONObject.parseObject(body);
			ElementCategory category = ElementCategory.getCategory(json.getString("category"));
			if(category==null){
				result.setErrorMsg("未找到ElementCategory");
				return result.toString();
			}
			BaseElement baseElement = category.getElementClass().newInstance();
			if(baseElement instanceof MultiArguments){
				List<String> fields = ((MultiArguments)baseElement).getFields();
				for(String f : fields){
					Object obj = json.get(f);
					if(obj instanceof JSONArray){
						break;
					}else{
						JSONArray array = new JSONArray();
						if(obj!=null) {
							array.add(obj);
						}
						json.remove(f);
						json.put(f, array);
					}
				}
			}
			UpdateResult updateResult = elementService.update(JSON.toJavaObject(json, category.getElementClass()));
			if(updateResult.getModifiedCount()>0) {
				BaseElement element = (BaseElement) elementService.findById(json.getString("id"), category.getElementClass());
				JSONObject object = new JSONObject();
				object.put("enabled", element.getEnabled());
				result.setObj(object);
				result.setStatus(Result.SUCCESS);
			}else{
				result.setErrorMsg("No match data!!!");
			}
		} catch (Exception e) {
			result.setErrorMsg(e.getMessage());
			log.error("update", e);
		}
		return result.toString();
	}

	/**
	 * 删除信息
	 * @param body
	 * @return
	 */
	@PostMapping("/delete")
	@ResponseBody
	public String delete(@RequestBody String body){
		Result<BaseElement> result = Result.errorResult();
		try {
			JSONObject json = JSONObject.parseObject(body);
			ElementCategory category = ElementCategory.getCategory(json.getString("category"));
			String id = json.getString("id");
			BaseElement baseElement = (BaseElement) elementService.findById(id, category.getElementClass());
			if(baseElement!=null){
				ElementTree tree = new ElementTree();
				tree.setElement(baseElement);
				this.findElementTree(tree);
				long counter = 0;
				this.recursionDelete(tree, counter);
				if(counter>0) {
					result.setStatus(Result.SUCCESS);
				}else{
					result.setErrorMsg("No match data!!!");
				}
			}
		} catch (Exception e) {
			result.setErrorMsg(e.getMessage());
			log.error("update", e);
		}
		return result.toString();
	}

	private void recursionDelete(ElementTree tree, long counter){
		BaseElement element = tree.getElement();
		ElementCategory category = ElementCategory.getCategory(element.getCategory());
		DeleteResult deleteResult = elementService.deleteById(element.getId(), category.getElementClass());
		counter += deleteResult.getDeletedCount();
		if(tree.getChildren().size()>0){
			for(ElementTree child : tree.getChildren()){
				this.recursionDelete(child, counter);
			}
		}
	}

	/**
	 * 移动节点
	 * @param move
	 * @return
	 */
	@PostMapping("/move")
	@ResponseBody
	public String move(@ModelAttribute MoveElement move){
		Result<BaseElement> result = Result.errorResult();
		BaseElement element = new BaseElement();
		element.setId(move.getId());
		element.setSort(move.getSort());
		try {
			elementService.update(element);
			element.setId(move.getMoveId());
			element.setSort(move.getMoveSort());
			elementService.update(element);
			result.setStatus(Result.SUCCESS);
		} catch (Exception e) {
			log.error("move", e);
		}
		return result.toString();
	}

	/**
	 * 修改排序号
	 * @param move
	 * @return
	 */
	@PostMapping("/modifySort")
	@ResponseBody
	public String modifySort(@ModelAttribute MoveElement move){
		Result<BaseElement> result = Result.errorResult();
		BaseElement element = new BaseElement();
		element.setId(move.getId());
		element.setSort(move.getSort());
		try {
			UpdateResult r = elementService.update(element);
			if(r.getModifiedCount()>0) {
				result.setStatus(Result.SUCCESS);
			}
		} catch (Exception e) {
			log.error("modifySort", e);
		}
		return result.toString();
	}

	/**
	 * 启用/禁止
	 * @param element
	 * @return
	 */
	@PostMapping("/enabled")
	@ResponseBody
	public String enabled(@ModelAttribute MoveElement element){
		Result<String> result = Result.errorResult();
		BaseElement updateEle = new BaseElement();
		updateEle.setId(element.getId());
		updateEle.setEnabled(element.isEnabled());
		try{
			UpdateResult r = elementService.update(updateEle);
			if(r.getModifiedCount()>0){
				BaseElement element1 = (BaseElement) elementService.findById(element.getId(), BaseElement.class);
				result.setObj(element1.getName());
				result.setStatus(Result.SUCCESS);
			}
		}catch(Exception e){
			log.error("enabled", e);
		}
		return result.toString();
	}

	/**
	 * 移动节点
	 * @param drag
	 * @return
	 */
	@PostMapping("/treeDrag")
	@ResponseBody
	public String treeDrag(@ModelAttribute MoveElement drag){
		Result<BaseElement> result = Result.errorResult();
		BaseElement updateEle = new BaseElement();
		updateEle.setId(drag.getId());
		updateEle.setParentId(drag.getParentId());
		try{
			UpdateResult r = elementService.update(updateEle);
			if(r.getModifiedCount()>0){
				result.setStatus(Result.SUCCESS);
			}
		}catch(Exception e){
			log.error("treeDrag", e);
		}
		return result.toString();
	}

	/**
	 * 粘贴元素
	 * @param element
	 * @return
	 */
	@PostMapping("/paste")
	@ResponseBody
	public String paste(@ModelAttribute MoveElement element, HttpServletRequest request){
		Result<MenuTree> result = Result.errorResult();
		if(StringUtils.isNotBlank(element.getId())){
			ElementCategory category = ElementCategory.getCategory(element.getCategory());
			if(category==null){
				result.setErrorMsg("Category类型不正确");
				return result.toString();
			}
			BaseElement baseElement = (BaseElement) elementService.findById(element.getId(), category.getElementClass());
			if(baseElement!=null){
				baseElement.setSort(element.getSort());
				ElementTree rootTree = new ElementTree();
				rootTree.setElement(baseElement);
				try{
					this.findElementTree(rootTree);
					UserIdentity identity = this.getSessionUser(request);
					this.recursionInsert(rootTree, element.getParentId(), element.getPlanId(), identity.getUserId());

					BaseElement beforeElement = (BaseElement) elementService.findById(element.getMoveId(), BaseElement.class);
					MenuTree rootMenuTree = this.createChildNode(rootTree.getElement(), beforeElement, null);
					this.recursionMenuTree(rootMenuTree, rootTree);
					result.setObj(rootMenuTree);
					result.setStatus(Result.SUCCESS);
				}catch (Exception e){
					result.setErrorMsg(e.getMessage());
					log.error("paste", e);
				}
			}
		}
		return result.toString();
	}

	private void findElementTree(ElementTree parentTree) throws Exception{
		BaseElement parentElement = parentTree.getElement();
		List<BaseElement> children = this.findChildren(parentElement.getId());
		if(!CollectionUtils.isEmpty(children)){
			for(BaseElement element : children){
				ElementTree childTree = new ElementTree();
				childTree.setElement(element);
				this.findElementTree(childTree);
				parentTree.addChildTree(childTree);
			}
		}
	}

	private List<BaseElement> findChildren(String parentId) throws Exception{
		ElementForm form = new ElementForm();
		form.setRows(0);
		form.setParentId(parentId);
		return elementService.selectListByForm(form, BaseElement.class);
	}

	private void recursionInsert(ElementTree rootTree, String parentId, String planId, String userId) throws Exception{
		BaseElement element = rootTree.getElement();
		element.setId(new ObjectId().toString());
		element.setParentId(parentId);
		element.setPlanId(planId);
		element.setUserId(userId);
		element.setCreateTime(System.currentTimeMillis());
		element.setModifyTime(null);
		elementService.insert(element);
		if(rootTree.getChildren().size()>0){
			for(ElementTree child : rootTree.getChildren()){
				this.recursionInsert(child, element.getId(), planId, userId);
			}
		}
	}

	private void recursionMenuTree(MenuTree parentTree, ElementTree parentElementTree){
		List<ElementTree> children = parentElementTree.getChildren();
		if(!CollectionUtils.isEmpty(children)){
			parentTree.setState("closed");
			for(int i=0; i<children.size(); i++){
				ElementTree child = children.get(i);
				BaseElement beforeChild = null, afterChild = null;
				if(i>0){
					beforeChild = children.get(i-1).getElement();
				}
				if(i<(children.size()-1)){
					afterChild = children.get(i+1).getElement();
				}
				MenuTree childTree = this.createChildNode(child.getElement(), beforeChild, afterChild);
				recursionMenuTree(childTree, child);
				parentTree.addChild(childTree);
			}
		}
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
}
